Skip to content

feat(PLU-441): add OAuth support to GitHub source connector#759

Merged
potter-potter merged 10 commits into
mainfrom
sean/plu-441-github-oauth
Jul 16, 2026
Merged

feat(PLU-441): add OAuth support to GitHub source connector#759
potter-potter merged 10 commits into
mainfrom
sean/plu-441-github-oauth

Conversation

@potter-potter

@potter-potter potter-potter commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Prepares the GitHub source connector for Foundation by adding OAuth auth, blob-SHA-based incremental change detection, and hardening indexing/downloading (API-call economy, tree-truncation handling, a download-path bug fix, consistent error classification, and rate-limit-aware retries). PAT configs continue to work unchanged.

What changed

  • OAuth support. GithubAccessConfig accepts oauth_token / refresh_token alongside the existing access_token (PAT), matching the field contract the platform's init-oauth-refresh container expects. oauth_token takes precedence; token refresh is handled upstream (PLU-381), not by the connector. PAT and OAuth are mutually exclusive.

  • Blob SHA as metadata.version. The indexer emits each file's git blob SHA (a content hash already in the tree listing — no extra call) so the platform's per-record incremental skip logic works correctly. The old tree ETag was shared across every file and couldn't drive per-file skip. metadata.date_modified is no longer set (git trees carry no reliable per-file timestamp; version is the sole skip signal).

  • API-call economy + tree truncation. Fetches the repo/default-branch once per run instead of twice per file (2N+32 calls). When GitHub truncates the recursive tree (~100k-entry / 7 MB cap, non-paginable), falls back to a per-directory walk so large repos index completely. Skips empty files, directories, and submodules. The downloader caches the repo object per instance (i.e. per process/pod), so repeated downloads in one worker skip the redundant GET /repos/{owner}/{repo} — safe under pod fan-out since nothing is shared across processes.

  • Download-path fix. GithubDownloader.run now creates the parent directory chain before writing — previously every download of a nested (or root) path failed with FileNotFoundError.

  • Error classification + retries. GitHub errors map consistently (auth 401/403 → UserAuthError, throttle → RateLimitError, 5xx → ProviderError; network resets/timeouts → SourceConnectionNetworkError). Transient throttles / 5xx / network errors are retried with exponential backoff that honors GitHub's Retry-After (secondary limit) and x-ratelimit-reset (primary limit), capped at 300s. A 403 with an exhausted window is treated as a retriable throttle, not a permanent auth failure. Attempt budget via max_retries (default 10) on both indexer and downloader configs. The raw-blob fetch now uses a bounded request timeout. Adds tenacity to the github extra.

Testing

  • Unit: new test/unit/connectors/test_github.py (61 cases) covering auth config, error classification, blob-SHA versioning, recursive listing + truncation walk, downloader caching/fetch, backoff parsing, and the retry loop. All pass in ~0.2s (retries mocked to not sleep).
  • Integration: test/integration/connectors/test_github.py refactored to share a validation helper; added test_github_source_oauth exercising the OAuth token path (requires GH_OAUTH_ACCESS_TOKEN). Existing PAT test preserved.
  • ruff check clean.

Notes

  • Version bumped to 1.7.1; tenacity added to the github extra with a matching uv.lock edit (uv lock couldn't be run in-sandbox due to the Azure-CLI auth requirement, so the lock was updated by hand to match uv's format — worth a quick uv lock confirmation in CI).
  • Companion changes for incremental support + OAuth refresh live in platform-etl-orchestration (etl_job_api incremental registry, etl-operator GithubOAuthRefresher).

SeanUnstructured and others added 2 commits July 14, 2026 14:01
Add oauth_token/refresh_token to GithubAccessConfig alongside the existing access_token (PAT), matching the init-oauth-refresh field contract. get_client prefers oauth_token; refresh is handled upstream, not in the connector (PLU-381). PAT and OAuth are mutually exclusive and PAT configs work unchanged.

Classify GitHub API errors: 401/403 -> UserAuthError (reconnect-required), 429/rate-limit -> RateLimitError, 5xx -> ProviderError (fixes >500 off-by-one). Add unit tests and an OAuth integration variant; bump to 1.6.29.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…auth

Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	CHANGELOG.md
#	unstructured_ingest/__version__.py
#	unstructured_ingest/processes/connectors/github.py
Set metadata.version to each file's git blob SHA (element.sha) instead of
the tree-response ETag (element.etag). The blob SHA is a content hash already
present in the recursive tree listing (no extra API call) and changes iff a
file's content changes, which is what the platform's per-record incremental
skip logic compares on. The ETag was shared across every file and bumped on
any repo change, so it could not drive per-file skip.

Null-guard metadata.date_modified (derived from the tree response's
Last-Modified header, which the git-data API may omit) so indexing no longer
crashes on None. Add unit tests for version and date_modified handling.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@potter-potter
potter-potter requested a review from a team as a code owner July 16, 2026 20:32

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 7 files

Shadow auto-approve: would not auto-approve because issues were found.

Re-trigger cubic

Comment thread unstructured_ingest/processes/connectors/github.py Outdated
Comment thread unstructured_ingest/processes/connectors/github.py Outdated

@dizon-u10d dizon-u10d left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t see any blocking issues here. The OAuth/PAT validation, GitHub error classification, retry handling, tree truncation fallback, blob-SHA versioning, and nested-path download fix all line up with the intended behavior.

I also checked the existing PR review state first; there are no prior reviews or unresolved changes-requested blockers to account for.

Validated locally with:

  • uv run --extra github --group test pytest test/unit/connectors/test_github.py -q
  • uv run --extra github --group lint ruff check unstructured_ingest/processes/connectors/github.py test/unit/connectors/test_github.py test/integration/connectors/test_github.py
  • git diff --check origin/main...HEAD

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 2 files (changes from recent commits).

Shadow auto-approve: would require human review. Adds OAuth authentication and adjusts operational tradeoffs (retry backoff, version metadata change), affecting authorization policy and output contract. Requires human review.

Re-trigger cubic

@potter-potter
potter-potter merged commit 5c0a3de into main Jul 16, 2026
36 checks passed
@potter-potter
potter-potter deleted the sean/plu-441-github-oauth branch July 16, 2026 21:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants